// file:kernel/fsif.c
// autor:jiang xinpeng
// time:2020.12.23
// update: 2021.11.28
// copyright:(C) 2020-2050 by jiang xinpeng,All right are reserved.

#include <os/fsal.h>
#include <os/fsalif.h>
#include <os/dir.h>
#include <os/file.h>
#include <os/path.h>
#include <os/driver.h>
#include <os/debug.h>
#include <os/account.h>
#include <os/schedule.h>
#include <os/memspace.h>
#include <os/vmm.h>
#include <os/fd.h>
#include <os/safety.h>
#include <os/dirent.h>
#include <lib/errno.h>
#include <os/pipe.h>
#include <sys/status.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/fcntl.h>

int SysOpen(char *path, int flags)
{
    int handle;

    if (!path)
        return -EINVAL;
    if (!fsif.open)
        return -ENOSYS;
    if (!AccountSelfPermissionCheck(path, PERMISS_ATTR_FILE))
    {
        handle = fsif.open(path, flags);
        if (handle < 0)
            return -ENOFILE;
        // install to kernel fd
        return FsFdInstall(handle, FILE_FD_KERNEL);
    }
    return -EPERM;
}

int SysClose(int fd)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->close)
        return -ENOSYS;
    if (!ffd->fsal->close(ffd->handle))
        return FsFdUnInstall(fd);
    return -1;
}

int SysRead(int fd, void *buff, size_t bytes)
{
    file_fd_t *ffd;


    if (fd < 0 || !buff || bytes < 0)
        return -EINVAL;

    ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->read)
        return -ENOSYS;

    return ffd->fsal->read(ffd->handle, buff, bytes);
}

int SysWrite(int fd, void *buff, size_t bytes)
{
    file_fd_t *ffd;

    if (fd < 0 || !buff || bytes < 0)
        return -EINVAL;
    ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->write)
        return -ENOSYS;
    return ffd->fsal->write(ffd->handle, buff, bytes);
}

int SysIoCtl(int fd, int cmd, void *arg)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;

    if (!ffd->fsal->ioctl)
        return -ENOSYS;

    return ffd->fsal->ioctl(ffd->handle, cmd, arg);
}

int SysFastIo(int fd, int cmd, void *arg)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->fastio)
        return -ENOSYS;
    return ffd->fsal->fastio(ffd->handle, cmd, arg);
}

int SysFastRead(int fd, void *buff, size_t bytes)
{
    file_fd_t *ffd = FdToFile(fd);
    if (fd < 0 || !bytes || !buff)
        return -EINVAL;
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->fastread)
        return -ENOSYS;
    return ffd->fsal->fastread(ffd->handle, buff, bytes);
}

int SysFastWrite(int fd, void *buff, size_t bytes)
{
    file_fd_t *ffd = FdToFile(fd);
    if (fd < 0 || !bytes || !buff)
        return -EINVAL;
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->fastwrite)
        return -ENOSYS;
    return ffd->fsal->fastwrite(ffd->handle, buff, bytes);
}

int SysChmod(const char *path, mode_t mode)
{
    char abs[MAX_PATH_LEN+1];

    if (!path)
        return -EINVAL;
    if (SafetyCheckRange((void *)path, MAX_PATH_LEN) < 0)
        return -EINVAL;
    if (!AccountSelfPermissionCheck((char *)path, PERMISS_ATTR_FILE))
        return -EPERM;
    BuildPath(path, abs);
    return fsif.chmod(abs, mode);
}

int SysFChmod(int fd, mode_t mode)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->fchmod)
        return ENOSYS;
    return ffd->fsal->fchmod(ffd->handle, mode);
}

dir_t SysOpenDir(const char *path)
{
    if (!path)
        return -EINVAL;
    if (!fsif.opendir)
        return -ENOSYS;
    if (!AccountSelfPermissionCheck(path, PERMISS_ATTR_FILE))
        return fsif.opendir(path);
    return -EPERM;
}

int SysCloseDir(dir_t dir)
{
    if (!fsif.closedir)
        return -ENOSYS;
    return fsif.closedir(dir);
}

int SysReadDir(dir_t dir, dirent_t *dirent)
{
    if (!dirent)
        return -EINVAL;
    if (!fsif.readdir)
        return -ENOSYS;
    return fsif.readdir(dir, dirent);
}

int SysMkfs(char *dev, char *fstype, uint32_t flags)
{
    if (!dev || !fstype)
        return -EINVAL;
    if (!fsif.mkfs)
        return -ENOSYS;
    if (!AccountSelfPermissionCheck(dev, PERMISS_ATTR_DEVICE))
        return fsif.mkfs(dev, fstype, flags);
    return -EPERM;
}

int SysRewindDir(dir_t dir)
{
    if (!fsif.rewinddir)
        return -ENOSYS;
    return fsif.rewinddir(dir);
}

int SysRewind(int fd)
{
    if(fd<0)
        return -EINVAL;
    file_fd_t *ffd=FdToFile(fd);
    if(!ffd)
        return -EINVAL;
    if(!ffd->fsal->rewind)
        return -ENOSYS;
    return ffd->fsal->rewind(ffd->handle);
}

int SysStatus(const char *path, status_t *status)
{
    char abspath[MAX_PATH_LEN+1];

    if (!path || !status)
        return -EINVAL;
    if (SafetyCheckRange((void *)path, MAX_PATH_LEN) < 0)
        return -EINVAL;
    if (SafetyCheckRange(status, sizeof(status_t)) < 0)
        return -EINVAL;
    if (AccountSelfPermissionCheck(path, PERMISS_ATTR_FILE) < 0)
        return -EPERM;

    BuildPath(path, abspath);
    return fsif.status(abspath, status);
}

int SysFStatus(int fd, status_t *status)
{
    file_fd_t *file = FdToFile(fd);
    if (!status)
        return -EINVAL;
    if (FSAL_FILE_BAD_IDX(fd))
        return -EINVAL;
    if (!file->fsal->fstatus)
        return -ENOSYS;
    return file->fsal->fstatus(file->handle, status);
}

int SysFtruncate(int fd, offset_t off)
{
    file_fd_t *file = FdToFile(fd);
    if (FSAL_FILE_BAD_IDX(fd))
        return -EINVAL;
    if (!file->fsal->ftruncate)
        return -EINVAL;
    return file->fsal->ftruncate(file->handle, off);
}

int SysFcntl(int fd, int cmd, int64_t arg)
{
    file_fd_t *file = FdToFile(fd);
    int newfd;

    if (FSAL_FILE_BAD_IDX(fd))
        return -EINVAL;
    switch (cmd)
    {
    case F_DUPFD:
    {
        if (file->fsal->incref(file->handle) < 0)
            return -EINVAL;
        newfd = FsFdInstallBase(file->handle, file->flags & FILE_FD_TYPE_MASK, arg);
        return newfd;
    }
    case F_GETFD:
    {
        arg = file->handle;
        return arg;
    }
    break;
    case F_SETFD:
    {
        file->handle = arg;
    }
    break;

    default:
        break;
    }
}

int SysPipe(int fd[2])
{
    if (!fd)
        return -EINVAL;
    if (MemCopyToUser(fd, NULL, sizeof(int) * 2) < 0)
        return -EINVAL;
    pipe_t *pipe = CreatePipe();
    if (!pipe)
        return -EINVAL;
    int rfd = FsFdInstall(pipe->id, FILE_FD_PIPE0);
    if (rfd < 0)
    {
        DestroyPipe(pipe);
        return -EAGAIN;
    }
    int wfd = FsFdInstall(pipe->id, FILE_FD_PIPE1);
    if (wfd < 0)
    {
        DestroyPipe(pipe);
        return -EAGAIN;
    }
    fd[0] = rfd;
    fd[1] = wfd;
    return 0;
}

int SysDup(int oldfd)
{
    file_fd_t *ffd = FdToFile(oldfd);
    int newfd;

    if (FILE_FD_BAD(ffd))
        return -EINVAL;

    if (!ffd->fsal->incref)
        return -ENOSYS;
    if (ffd->fsal->incref(ffd->handle) < 0)
        return -EINVAL;
    newfd = FsFdInstall(ffd->handle, ffd->flags & FILE_FD_TYPE_MASK);
    //KPrint("dup oldfd %d handle %d newfd %d newhandle %d flags %d\n",oldfd,ffd->handle,newfd,FdToFile(newfd)->handle);
    return newfd;
}

int SysDup2(int oldfd, int newfd)
{
    file_fd_t *ffd = FdToFile(oldfd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (oldfd == newfd) // fd same
        return newfd;

    //KPrint("sysdup2: old fd %d new fd %d\n",oldfd,newfd);

    file_fd_t *newffd = FdToFile(newfd);
    if (newffd && newffd->handle >= 0 && newffd->flags)
    {
        SysClose(newfd);    //maybe closed error because fd dup has been closed then closing repeat
    }
    if (!ffd->fsal->incref)
        return -ENOSYS;

    if (!ffd->fsal->incref(ffd->handle) < 0)
        return -EINVAL;

    //KPrint("sys_dup2: src %d handle %d new %d\n",oldfd,ffd->handle,newfd);

    newfd = FsFdInstallTo(ffd->handle, newfd, ffd->flags & FILE_FD_TYPE_MASK);
    return newfd;
}

int SysProbeDev(const char *name, char *buff, size_t len)
{
    if (!name || !buff)
        return -EINVAL;

    if (SafetyCheckRange(buff, len) < 0)
        return -EPERM;

    return DeviceProbeUnUsed(name, buff, len);
}

int SysMount(char *source, char *targe, char *fstype, uint32_t flags)
{
    char abs_source[MAX_PATH_LEN+1];
    char abs_targe[MAX_PATH_LEN+1];

    if (!source || !targe || !fstype)
        return -EINVAL;
    if (SafetyCheckRange(source, MAX_PATH_LEN))
        return -EINVAL;
    if (SafetyCheckRange(targe, MAX_PATH_LEN))
        return -EINVAL;
    if (SafetyCheckRange(fstype, 32))
        return -EINVAL;

    if (AccountSelfPermissionCheck(source, PERMISS_ATTR_DEVICE) < 0)
        return -EPERM;

    BuildPath(source, abs_source);
    BuildPath(targe, abs_targe);
    return fsif.mount(abs_source, abs_targe, fstype, flags);
}

int SysUnMount(char *path, uint32_t flags)
{
    char _path[MAX_PATH_LEN+1];

    if (!path)
        return -EINVAL;
    if (SafetyCheckRange(path, MAX_PATH_LEN))
        return -EINVAL;
    if (AccountSelfPermissionCheck(path, PERMISS_ATTR_DEVICE) < 0)
        return -EPERM;
    BuildPath(path, _path);
    return fsif.unmount(path, flags);
}

int SysUnlink(char *path)
{
    char _path[MAX_PATH_LEN+1];

    if (!path)
        return -EINVAL;
    if (SafetyCheckRange(path, MAX_PATH_LEN))
        return -EINVAL;
    if (AccountSelfPermissionCheck(path, PERMISS_ATTR_DEVICE) < 0)
        return -EPERM;
    BuildPath(path, _path);
    return fsif.unlink(path);
}

int FsIfIncRef(int fd)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FSAL_FILE_BAD_FILE(ffd))
        return -EINVAL;
    if (!ffd->fsal->incref)
        return -ENOSYS;
    return ffd->fsal->incref(ffd->handle);
}

int FsIfDecRef(int fd)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FSAL_FILE_BAD_FILE(ffd))
        return -EINVAL;
    if (!ffd->fsal->decref)
        return -ENOSYS;
    return ffd->fsal->decref(ffd->handle);
}

int SysGetCwd(char *buff, int size)
{
    task_t *cur = cur_task;

    if (!buff)
        return -EINVAL;
    if (!cur->fileman)
        return -EINVAL;
    if (MemCopyToUser(buff, cur->fileman->cwd, size == 0 ? MAX_PATH_LEN : size) < 0)
        return -EINVAL;
    return 0;
}

int SysMkDir(const char *path, mode_t mode)
{
    char abs_path[MAX_PATH_LEN+1];

    if (!path)
        return -EINVAL;
    if (SafetyCheckRange((void *)path, MAX_PATH_LEN) < 0)
        return -EINVAL;
    if (AccountSelfPermissionCheck(path, PERMISS_ATTR_FILE) < 0)
        return -EPERM;
    BuildPath(path, abs_path);
    return fsif.mkdir(abs_path, mode);
}

int SysRmDir(const char *path, mode_t mode)
{
    char abs_path[MAX_PATH_LEN+1];

    if (!path)
        return -EINVAL;
    if (SafetyCheckRange((void *)path, MAX_PATH_LEN) < 0)
        return -EINVAL;
    if (AccountSelfPermissionCheck(path, PERMISS_ATTR_FILE) < 0)
        return -EPERM;
    BuildPath(path, abs_path);
    return fsif.rmdir(abs_path);
}

int SysRename(const char *source, const char *targe)
{
    char abs_source[MAX_PATH_LEN+1];
    char abs_targe[MAX_PATH_LEN+1];

    if (!source || !targe)
        return -EINVAL;
    if (SafetyCheckRange((void *)source, MAX_PATH_LEN) < 0)
        return -EINVAL;
    if (SafetyCheckRange((void *)targe, MAX_PATH_LEN) < 0)
        return -EINVAL;
    if (AccountSelfPermissionCheck(source, PERMISS_ATTR_FILE) < 0)
        return -EPERM;
    if (AccountSelfPermissionCheck(targe, PERMISS_ATTR_FILE) < 0)
        return -EPERM;
    BuildPath(source, abs_source);
    BuildPath(targe, abs_targe);
    return fsif.rename(source, targe);
}

int SysAccess(const char *path, mode_t mode)
{
    char abs_path[MAX_PATH_LEN+1];
    memset(abs_path,0,MAX_PATH_LEN+1);

    if (!path)
        return -EINVAL;
    if (SafetyCheckRange((void *)path, MAX_PATH_LEN) < 0)
        return -EINVAL;
    if (AccountSelfPermissionCheck(path, PERMISS_ATTR_FILE) < 0)
        return -EPERM;
    BuildPath(path, abs_path);

    return fsif.access(abs_path, mode);
}

void *__SysMap(void *addr, size_t len, int prot, int flags, int fd, offset_t off)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return NULL;
    if (!ffd->fsal->mmap)
        return NULL;
    return ffd->fsal->mmap(ffd->handle, addr, len, prot, flags, off);
}

void *SysMmap(mmap_args_t *args)
{
    return __SysMap(args->addr, args->len, args->prot, args->flags, args->fd, args->off);
}

int SysChDir(const char *path)
{
    task_t *cur = cur_task;
    dir_t dir;

    if (!path)
        return -EINVAL;
    if (SafetyCheckRange((void *)path, MAX_PATH_LEN) < 0)
        return -EINVAL;
    if (!cur->fileman)
        return -EINVAL;
    dir = SysOpenDir(path);
    if (dir < 0)
        return -ENOFILE;
    return TaskSetCwd(cur, path);
}

int SysLSeek(int fd, offset_t off, int whence)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->lseek)
        return -ENOSYS;
    uint32_t res= ffd->fsal->lseek(fd, off, whence);
    return res;
}

int SysTell(int fd)
{
    file_fd_t *ffd = FdToFile(fd);
    if (FILE_FD_BAD(ffd))
        return -EINVAL;
    if (!ffd->fsal->ftell)
        return -ENOSYS;
    return ffd->fsal->ftell(fd);
}

void FdSetOr(fd_set_t *dst, fd_set_t *src, int max)
{
    int i;
    for (i = 0; i < max; i += 8)
    {
        dst->fd_bits[i / 8] |= src->fd_bits[i / 8];
    }
}

int FdSetEmpty(fd_set_t *set, int max)
{
    int i;
    for (i = 0; i < max; i++)
    {
        if (FD_ISSET(i, set))
            return 0;
    }
    return 1;
}

void FdSetDump(fd_set_t *set, int max)
{
    int i;
    KPrint("\n---FD SET DUMP-----\n");
    for (i = 0; i < max; i++)
    {
        if (FD_ISSET(i, set))
            KPrint("1");
        else
            KPrint("0");
    }
    KPrint("\n");
}

static int DoSelectCheckFds(int max, fd_set_t *readfds, fd_set_t *writefds, fd_set_t *exceptfds)
{
    int i;
    file_fd_t *ffd;

    for (i = 0; i < max; i++)
    {
        if (readfds)
        {
            if (FD_ISSET(i, readfds))
            {
                ffd = FdToFile(i);
                if (FILE_FD_BAD(ffd))
                {
                    KPrint("%s: fd %d error! handle=%d flags=%x\n", __func__, i, ffd->handle, ffd->flags);
                    return -1;
                }
            }
        }
        if (writefds)
        {
            if (FD_ISSET(i, writefds))
            {
                ffd = FdToFile(i);
                if (FILE_FD_BAD(ffd))
                {
                    KPrint("%s: fd %d error! handle=%d flags=%x\n", __func__, i, ffd->handle, ffd->flags);
                    return -1;
                }
            }
        }
        if (exceptfds)
        {
            if (FD_ISSET(i, exceptfds))
            {
                ffd = FdToFile(i);
                if (FILE_FD_BAD(ffd))
                {
                    KPrint("%s: fd %d error! handle=%d flags=%x\n", __func__, i, ffd->handle, ffd->flags);
                    return -1;
                }
            }
        }
    }
    return 0;
}

static int DoSelect(int max, fd_set_t *readfds, fd_set_t *writefds, fd_set_t *exceptfds, timeval_t *timeout)
{
    fd_set_t readfds_tab[SELECT_FDS_NUM], writefds_tab[SELECT_FDS_NUM], exceptfds_tab[SELECT_FDS_NUM];
    select_t selects[SELECT_FDS_NUM] = {
        fsif.select,
        pipeif_rd.select,
        pipeif_wr.select,
        netif_fsal.select,
    };
    file_fd_t *ffd;
    int total=0;
    int i, j;

    if (DoSelectCheckFds(max, readfds, writefds, exceptfds) < 0)
        return -EBADF;

    // clear buffer
    for (i = 0; i < SELECT_FDS_NUM; i++)
    {
        FD_ZERO(&readfds_tab[i]);
        FD_ZERO(&writefds_tab[i]);
        FD_ZERO(&exceptfds_tab[i]);
    }

    // alloc the fd to targe set according type
    for (j = 0; j < SELECT_FDS_NUM; j++) // set
    {
        for (i = 0; i < max; i++) // fd
        {
            if (readfds)
            {
                if (FD_ISSET(i, readfds))
                {
                    ffd = FdToFile(i);
                    switch (ffd->flags & FILE_FD_TYPE_MASK)
                    {
                    case FILE_FD_KERNEL:
                        if (j != 0)
                            break;
                        FD_SET(i, &readfds_tab[j]);
                        break;
                    case FILE_FD_PIPE0:
                        if (j != 1)
                            break;
                        FD_SET(i, &readfds_tab[j]);
                        break;
                    case FILE_FD_PIPE1:
                        if (j != 2)
                            break;
                        FD_SET(i, &readfds_tab[j]);
                        break;
                    case FILE_FD_SOCKET:
                        if (j != 3)
                            break;
                        FD_SET(i, &readfds_tab[j]);
                        break;
                    default:
                        KPrint("%s: unknow fd %d file type!\n", __func__, i);
                        return -EBADF;
                    }
                }
            }
            if (writefds)
            {
                if (FD_ISSET(i, writefds))
                {
                    ffd = FdToFile(i);
                    switch (ffd->flags & FILE_FD_TYPE_MASK)
                    {
                    case FILE_FD_KERNEL:
                        if (j != 0)
                            break;
                        FD_SET(i, &writefds_tab[j]);
                        break;
                    case FILE_FD_PIPE0:
                        if (j != 1)
                            break;
                        FD_SET(i, &writefds_tab[j]);
                        break;
                    case FILE_FD_PIPE1:
                        if (j != 2)
                            break;
                        FD_SET(i, &writefds_tab[j]);
                        break;
                    case FILE_FD_SOCKET:
                        if (j != 3)
                            break;
                        FD_SET(i, &writefds_tab[j]);
                        break;
                    default:
                        KPrint("%s: unknow fd %d file type!\n", __func__, i);
                        return -EBADF;
                    }
                }
            }
            if (exceptfds)
            {
                if (FD_ISSET(i, exceptfds))
                {
                    ffd = FdToFile(i);
                    switch (ffd->flags & FILE_FD_TYPE_MASK)
                    {
                    case FILE_FD_KERNEL:
                        if (j != 0)
                            break;
                        FD_SET(i, &exceptfds_tab[j]);
                        break;
                    case FILE_FD_PIPE0:
                        if (j != 1)
                            break;
                        FD_SET(i, &exceptfds_tab[j]);
                        break;
                    case FILE_FD_PIPE1:
                        if (j != 2)
                            break;
                        FD_SET(i, &exceptfds_tab[j]);
                        break;
                    case FILE_FD_SOCKET:
                        if (j != 3)
                            break;
                        FD_SET(i, &exceptfds_tab[j]);
                        break;
                    default:
                        KPrint("%s: unknow fd %d file type!\n", __func__, i);
                        return -EBADF;
                    }
                }
            }
        }
    }

    // set zero to old set
    if (readfds)
        FD_ZERO(readfds);
    if (writefds)
        FD_ZERO(writefds);
    if (exceptfds)
        FD_ZERO(exceptfds);

    for (i = 0; i < SELECT_FDS_NUM; i++)
    {
        if (selects[i]) // interface support select
        {
            if (FdSetEmpty(&readfds_tab[i], max) && FdSetEmpty(&writefds_tab[i], max) && FdSetEmpty(&exceptfds_tab[i], max))
                continue;
            // call interface
            int ret = selects[i](max, &readfds_tab[i], &writefds_tab[i], &exceptfds_tab[i], timeout);
            if (ret < 0)
                return ret;
            // write back
            if (readfds)
            {
                FdSetOr(readfds, &readfds_tab[i], max);
            }
            if (writefds)
            {
                FdSetOr(writefds, &writefds_tab[i], max);
            }
            if (exceptfds)
            {
                FdSetOr(exceptfds, &exceptfds_tab[i], max);
            }
            total += ret;
        }
    }
    return total;
}

int SysSelect(int max, fd_set_t *readfds, fd_set_t *writefds, fd_set_t *exceptfds, timeval_t *timeout)
{
    fd_set_t _readfds, _writefds, _exceptfds;
    timeval_t _timeout;

    if (max < 0 || max > LOCAL_FILE_MAX)
        return -EINVAL;

    if (readfds)
    {
        if (MemCopyFromUser(&_readfds, readfds, sizeof(fd_set_t)) < 0)
            return -EINVAL;
    }
    if (writefds)
    {
        if (MemCopyFromUser(&_writefds, writefds, sizeof(fd_set_t)) < 0)
            return -EINVAL;
    }
    if (exceptfds)
    {
        if (MemCopyFromUser(&_exceptfds, exceptfds, sizeof(fd_set_t)) < 0)
            return -EINVAL;
    }
    if (timeout)
    {
        if (MemCopyFromUser(&_timeout, timeout, sizeof(timeval_t)) < 0)
            return -EINVAL;
    }

    int ret = DoSelect(max, readfds ? &_readfds : NULL, writefds ? &_writefds : NULL, exceptfds ? &_exceptfds : NULL, timeout ? &_timeout : NULL);

    if (readfds)
    {
        if (MemCopyToUser(readfds, &_readfds, sizeof(fd_set_t)) < 0)
            return -EINVAL;
    }
    if (writefds)
    {
        if (MemCopyToUser(writefds, &_writefds, sizeof(fd_set_t)) < 0)
            return -EINVAL;
    }
    if (exceptfds)
    {
        if (MemCopyToUser(exceptfds, &_exceptfds, sizeof(fd_set_t)) < 0)
            return -EINVAL;
    }
    return ret;
}
